package com.you.weixinpay.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.you.weixinpay.enums.WxNotifyType;
import com.you.weixinpay.properties.WeChatProperties;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.UUID;

/**
 * Native支付工具类
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class WeChatNativePayUtil {

    @Resource
    private final CloseableHttpClient httpClient;
    private final WeChatProperties weChatProperties;
    private final Verifier verifier;
    @Resource
    private CloseableHttpClient wxPayNoSignClient; //无需应答签名

    public static final String NATIVE = "https://api.mch.weixin.qq.com/v3/pay/transactions/native";
    public static final String REFUND_URL = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";

    /**
     * 创建Native支付订单
     *
     * @param orderNo     商户订单号
     * @param total       金额(元)
     * @param description 商品描述
     * @return 支付二维码链接
     */
    public JSONObject createOrder(String orderNo, BigDecimal total, String description) throws Exception {

        JSONObject request = new JSONObject();
        request.put("appid", weChatProperties.getAppid());
        request.put("mchid", weChatProperties.getMchid());
        request.put("description", description);
        request.put("out_trade_no", orderNo);
        request.put("notify_url", weChatProperties.getNotifyUrl().concat(WxNotifyType.NATIVE_NOTIFY.getType()));
//        request.put("time_expire", Instant.now().plus(Duration.ofMinutes(Long.parseLong(weChatProperties.getOrderExpireTime()))).toString());

        JSONObject amount = new JSONObject();
        amount.put("total", total.multiply(new BigDecimal(100)).intValue()); //微信支付以分为单位
        amount.put("currency", "CNY");
        request.put("amount", amount);

        // 创建请求【WeChatPayConfig】已经执行了签名
        HttpPost httpPost = new HttpPost(NATIVE);
        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString()); //("Accept", "application/json");
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()); //("Content-Type", "application/json");
        httpPost.setEntity(new StringEntity(request.toJSONString(), StandardCharsets.UTF_8));

        // 执行请求
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();
            log.info("微信返回的数据: {}", bodyAsString);
            log.info("微信返回的响应状态码: {}", statusCode);
            if (statusCode == 200) {
                //解析返回的json数据
                return JSON.parseObject(bodyAsString);
            } else {
                log.error("获取二维码失败");
                return null;
            }
        }
    }

    /**
     * 关闭订单
     *
     * @param orderNo 商户订单号
     * @throws Exception 关闭订单异常
     */
    public void closeOrder(String orderNo) throws Exception {
        String url = String.format("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s/close", orderNo);

        JSONObject request = new JSONObject();
        request.put("mchid", weChatProperties.getMchid());

        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        httpPost.setEntity(new StringEntity(request.toJSONString(), StandardCharsets.UTF_8));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            int statusCode = response.getStatusLine().getStatusCode(); //响应状态码
            if (statusCode == 204) {
                log.info("微信关单成功，订单号：{}", orderNo);
            } else {
                String responseBody = EntityUtils.toString(response.getEntity());
                log.error("微信关单失败，响应状态码：{}，响应内容：{}", statusCode, responseBody);
                throw new RuntimeException("微信关单失败：" + responseBody);
            }
        }
    }

    /**
     * 申请退款
     *
     * @param outTradeNo  商户订单号
     * @param outRefundNo 商户退款单号
     * @param total       订单金额(元)
     * @param refund      退款金额(元)
     * @param reason      退款原因
     * @return 退款结果
     */
    public JSONObject refund(String outTradeNo, String outRefundNo, BigDecimal total, BigDecimal refund, String reason) throws Exception {
        JSONObject request = new JSONObject();
        request.put("out_trade_no", outTradeNo);
        request.put("out_refund_no", outRefundNo);
        request.put("notify_url", weChatProperties.getRefundNotifyUrl().concat(WxNotifyType.REFUND_NOTIFY.getType()));

        JSONObject amount = new JSONObject();
        amount.put("refund", refund.multiply(new BigDecimal(100)).intValue()); // 退款金额(分)
        amount.put("total", total.multiply(new BigDecimal(100)).intValue()); // 原订单金额(分)
        amount.put("currency", "CNY");
        request.put("amount", amount);

        if (reason != null && !reason.isEmpty()) {
            request.put("reason", reason);
        }

        HttpPost httpPost = new HttpPost(REFUND_URL);
        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        httpPost.setEntity(new StringEntity(request.toJSONString(), StandardCharsets.UTF_8));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();
            log.info("微信退款返回数据: {}", bodyAsString);
            log.info("微信退款响应状态码: {}", statusCode);
            if (statusCode == 200) {
                return JSON.parseObject(bodyAsString);
            } else {
                throw new RuntimeException("退款申请失败: " + bodyAsString);
            }
        }
    }

    /**
     * 查询支付订单状态
     *
     * @param orderNo 商户订单号
     * @return 订单状态
     */
    public String queryOrder(String orderNo) throws Exception {
        String url = String.format("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s?mchid=%s",
                orderNo, weChatProperties.getMchid());

        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String responseBody = EntityUtils.toString(response.getEntity());
            if (response.getStatusLine().getStatusCode() == 200) {
                JSONObject result = JSON.parseObject(responseBody);
                log.info("完整【支付订单】查询结果: {}", result);
                String tradeState = result.getString("trade_state");
                log.info("微信订单【状态】查询结果，订单号：{}，状态：{}", orderNo, tradeState);
                return tradeState;
            } else {
                log.error("查询订单状态失败, 响应: {}", responseBody);
                throw new RuntimeException("查询订单状态失败: " + responseBody);
            }
        }
    }

    /**
     * 查询退款状态
     *
     * @param refundNo 商户退款单号
     * @return 退款状态
     */
    public String queryRefund(String refundNo) throws Exception {
        String url = String.format("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/%s", refundNo);

        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String responseBody = EntityUtils.toString(response.getEntity());
            if (response.getStatusLine().getStatusCode() == 200) {
                JSONObject result = JSON.parseObject(responseBody);
                log.info("完整【退款状态】查询结果: {}", result);
                log.info("【退款状态】查询结果: {}", result.getString("status"));
                return result.getString("status");
            } else {
                log.error("查询退款状态失败, 响应: {}", responseBody);
                throw new RuntimeException("查询退款状态失败: " + responseBody);
            }
        }
    }

    /**
     * 查询交易账单
     *
     * @param billDate 账单日期，格式：yyyy-MM-dd
     * @param billType 账单类型：ALL-全部，SUCCESS-成功，REFUND-退款
     * @return 账单下载URL
     */
    public String queryTradeBill(String billDate, String billType) throws Exception {
        String url = String.format("https://api.mch.weixin.qq.com/v3/bill/tradebill?bill_date=%s&bill_type=%s",
                billDate, billType);

        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String responseBody = EntityUtils.toString(response.getEntity());
            if (response.getStatusLine().getStatusCode() == 200) {
                JSONObject result = JSON.parseObject(responseBody);
                log.info("账单查询结果: {}", result);
                return result.getString("download_url");
            } else {
                log.error("查询交易账单失败, 响应: {}", responseBody);
                throw new RuntimeException("查询交易账单失败: " + responseBody);
            }
        }
    }

    /**
     * 下载账单【不需要签名】
     *
     * @param downloadUrl 账单下载URL
     * @return 账单内容
     */
    public String downloadBill(String downloadUrl) throws Exception {
        HttpGet httpGet = new HttpGet(downloadUrl);
        httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());

        try (CloseableHttpResponse response = wxPayNoSignClient.execute(httpGet)) {
            String responseBody = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                log.info("账单下载成功");
                return responseBody;
            } else if (statusCode == 204) {
                log.info("账单下载请求已接受，但无内容返回");
                return "";
            } else {
                log.error("下载账单失败, 响应: {}", responseBody);
                throw new RuntimeException("下载账单失败: " + responseBody);
            }
        }
    }


    /**
     * 验证签名 【【不能使用static,因为verify 非静态】】
     *
     * @param request     HTTP请求
     * @param requestBody 请求体内容
     * @return 是否验证通过
     */
    public boolean verifySignature(HttpServletRequest request, String requestBody) {
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signature = request.getHeader("Wechatpay-Signature");
        String serial = request.getHeader("Wechatpay-Serial");

        try {
            String message = timestamp + "\n" + nonce + "\n" + requestBody + "\n";
            return verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature);
        } catch (Exception e) {
            log.error("验证签名异常", e);
            return false;
        }
    }


    /**
     * 解密数据
     *
     * @param cipherText     密文
     * @param associatedData 附加数据
     * @param nonce          随机串
     * @return 解密后的数据
     * @throws GeneralSecurityException 解密异常
     */
    public String decryptData(String cipherText, String associatedData, String nonce) throws GeneralSecurityException {
        AesUtil aesUtil = new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        return aesUtil.decryptToString(
                associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                cipherText
        );
    }

    /**
     * 处理通知数据 【不能使用static,因为decryptData里的weChatProperties非静态】
     *
     * @param resource 资源对象
     * @return 解密后的数据
     * @throws GeneralSecurityException 解密异常
     */
    public String processResource(JSONObject resource) throws GeneralSecurityException {
        String cipherText = resource.getString("ciphertext");
        String associatedData = resource.getString("associated_data");
        String nonceStr = resource.getString("nonce");
        return decryptData(cipherText, associatedData, nonceStr);
    }

    /**
     * 构建成功响应
     *
     * @return 成功响应JSON字符串
     */
    public static String buildSuccessResponse() {
        JSONObject response = new JSONObject();
        response.put("code", "SUCCESS");
        response.put("message", "成功");
        return response.toJSONString();
    }

    /**
     * 构建成失败响应
     *
     * @return 失败响应JSON字符串
     */
    public static String buildFailureResponse() {
        JSONObject response = new JSONObject();
        response.put("code", "FAILURE");
        response.put("message", "失败");
        return response.toJSONString();
    }

    /**
     * 解析通知事件类型
     *
     * @param requestBody 请求体
     * @return 事件类型
     */
    public static String parseEventType(String requestBody) {
        JSONObject result = JSON.parseObject(requestBody);
        return result.getString("event_type");
    }

    /**
     * 获取通知资源对象
     *
     * @param requestBody 请求体
     * @return 资源对象
     */
    public static JSONObject getResource(String requestBody) {
        JSONObject result = JSON.parseObject(requestBody);
        return result.getJSONObject("resource");
    }

}